<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <button id="request">开始请求</button>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /* 
      对于这个请求控制器的理解，这其实就是用闭包和异步请求来处理这个并发请求的问题
      闭包可以保证变量私有化，不让外面的代码直接修改到函数中的变量，这个闭包好像还有共有化变量的意思
      传出去的内函数可以实现访问外函数中的变量，即使这个内函数被重复的使用了，但是这个变量却是同一个

      以下面这个例子为例：
      内函数中向队列中插入了一个回调函数，然后调用了dequeue函数，edqueue函数就会走一遍里面的逻辑，
      如果符合while的条件，也就是队列中还有回调函数，并且当前并发请求数没有达到最大值，
      就向下走，取出队列中的第一个回调函数，并且执行它，然后将当前并发请求数加一，这个是同步的
      当执行完回调函数中的同步代码之后就会返回一个promise对象，执行这个promise对象的异步操作，
      代码会继续往下走，指向下面的代码，
      只有当这个异步函数返回之后，就会执行resolve函数中的代码，然后执行finally中的代码，
      然后将当前并发请求数减一，然后再次调用dequeue函数，继续处理队列中的下一个请求
    */

    function fetchData(concurrency) {
      const queue = [];
      let maxCountRequest = concurrency || 5; // 默认最大并发请求数
      let currentRequest = 0; // 当前并发请求数

      const dequeue = () => {
        while (currentRequest < maxCountRequest && queue.length) {
          const request = queue.shift(); // 取出请求
          currentRequest++;
          request().then(res => {
            console.log("请求完成", res.data);
          }).catch(err => {
            console.log("请求失败", err);
          }).finally(() => {
            currentRequest--; // 请求完成，当前并发请求数减一
            dequeue(); // 处理下一个请求
          })
        }
      }

      return (req) => {
        queue.push(req); // 加入请求队列，push之后并不会自动的处理回调函数而是需要手动的调用一下dequeue函数
        // 后面可以考虑一下重写这个push函数，每当插入一个回调函数的时候就会自动的调用这个dequeue函数
        dequeue(); // 开始处理请求队列
      }
    }

    const enqueue = fetchData(6); // 创建请求队列，并发请求数为6

    // for (let i = 0; i < 100; i++) {
    //   enqueue(() => axios.get("http://localhost:3000/test"));
    // }
    const reqButton = document.querySelector("#request");
    reqButton.addEventListener("click", () => {
      for (let i = 0; i < 100; i++) {
        enqueue(() => axios.get("http://localhost:3000/test"));
        // axios.get("http://localhost:3000/test").then(res => {
        //   console.log("res", res.data);
        // }).catch(err => {
        //   console.log("err", err);
        // })
      }
    })
  </script>
</body>

</html>